package com.zhp.context;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.function.Supplier;

/**
 * 惰性删除过期时间map<br/>
 * 用小根堆 和 map，2种数据结构<br/>
 * 若达到指定容量，则移除过期时间最小的key<br/>
 *
 * @author zhanghp
 * @since 2024/5/20 17:51
 */
public class LazyExpireFixCapacityInstance {


    private static final Map<Object, TimedValueInfo<Object>> timedMap = new HashMap<>();

    private static final int capacity = 16;

    /**
     * 小根堆<br/>
     * todo: 用数组代替PriorityQueue（效率会有倍增的效果）
     */
    private static final PriorityQueue<TimedKeyInfo<Object>> heap = new PriorityQueue<>((a, b) -> {
        return Long.compare(a.getExpireTime(), b.getExpireTime());
    });


    public static Map<Object, TimedValueInfo<Object>> get() {
        return timedMap;
    }


    // O(logn)
    public static <K, V> V computeIfAbsent(K key, Supplier<V> value, Duration duration) {
        boolean remove =false;
        if (timedMap.containsKey(key)) {
            TimedValueInfo<Object> objectTimedValueInfo = timedMap.get(key);
            if (objectTimedValueInfo.getExpireTime() > System.currentTimeMillis()) {
                return (V) objectTimedValueInfo.value;
            }
            remove = true;
        }
        if (remove || timedMap.size() == capacity) {
            // 小跟堆处理 & 移除
            timedMap.remove(heap.poll().key);
        }
        V ans = value.get();
        long expireTime = System.currentTimeMillis() + duration.toMillis();


        timedMap.put(key, new TimedValueInfo<>(ans, expireTime));
        heap.add(new TimedKeyInfo<>(key, expireTime));
        // 小跟堆处理
        return ans;
    }

    @Data
    @AllArgsConstructor
    private static class TimedValueInfo<V> {
        private V value;

        private long expireTime;
    }

    @Data
    @AllArgsConstructor
    private static class TimedKeyInfo<K> {
        private K key;

        private long expireTime;
    }

}
